//markdown 中用到的API
const fs = require("fs");
const { ObjectId } = require("mongodb");
const collection = 'blog';
const userProfileCollection = 'userProfile';

/**
 * @description: 查询用户是否点赞了某篇博客  返回 '' / UP / DOWN 点赞还是点踩或者没有
 * @param {object{userId:string,blogId:string}}
 * @return {string}  '' / UP / DOWN
 */
const queryBlogThumbsState = (findData) => async (req, res) => {
    const { userId, blogId } = req.query;
    //获取该用户的  thumbsUp  thumbsDown 数组
    const result = await findData({
        collection: 'userProfile', find: {
            _id: ObjectId(userId)
        },
        project: { thumbsUp: 1, thumbsDown: 1 }
    })
    const { thumbsUp = [], thumbsDown = [] } = result[0];
    const type = thumbsUp.includes(blogId) ? 'UP' : thumbsDown.includes(blogId) ? 'DOWN' : '';
    return res.json(type);
}
/**
博客赞和踩状态的转换
 */
const transformThumbs = (updateOne) => async (req, res) => {
    const { userId, blogId, from, to } = req.payload;
    console.log(userId, blogId, from, to);
    const minusField = from === 'UP' ? 'thumbsUp' : "thumbsDown";
    const addField = minusField == "thumbsUp" ? "thumbsDown" : 'thumbsUp';
    const updateBlog = await updateOne({
        collection, _id: blogId, cusData: {
            $inc: {
                [minusField]: -1,
                [addField]: 1
            }
        },
    });
    const updateUserProfile = await updateOne({
        collection: userProfileCollection, _id: userId, cusData: {
            $pull: { [minusField]: blogId },
            $addToSet: { [addField]: blogId }
        },
    });
    if (updateUserProfile.acknowledged && updateBlog.acknowledged) {
        return res.json({ message: '操作成功', code: 0 });
    }
    res.end()
}

/**
 * @description: 取消博客点赞/踩记录 博客点赞数-1 用户点赞列表 - blogId
 */
const cancelThumbs = (updateOne) => (req, res) => {
    let data = '';
    req.on('data', (chunk) => { data += chunk });
    req.on('end', async () => {
        const { userId, blogId, type } = JSON.parse(data);
        const fields = type === 'UP' ? "thumbsUp" : "thumbsDown";
        console.log(`用户${userId}取消${blogId}的点${fields}记录`);
        const updateBlog = await updateOne({
            collection, _id: blogId, cusData: {
                $inc: { [fields]: -1 }
            },
        });
        if (!updateBlog.acknowledged) return res.end();
        const updateUserProfile = await updateOne({
            collection: userProfileCollection, _id: userId, cusData: {
                $pull: { [fields]: blogId },
            },
        });
        //如果更新用户失败 恢复对博客的更改
        if (!updateUserProfile.acknowledged) {
            updateOne({
                collection, _id: blogId, cusData: {
                    $inc: { [fields]: 1 }
                },
            })
            return res.end();
        }
        res.json({ message: '取消成功', code: 0 })
    })
}

//博客的点赞
const thumbsUpBlog = (updateOne) => (req, res) => {
    let data = '';
    req.on('data', (chunk) => { data += chunk });
    req.on('end', async () => {
        const { userId, blogId } = JSON.parse(data);
        await updateOne({
            collection, _id: blogId, cusData: {
                $inc: { thumbsUp: 1 }
            },
        });
        //更改用户点赞列表加入这篇博客id 实现下次进入亮起点赞图标
        // 点赞数组添加 点踩数组删除
        const result = await updateOne({
            collection: 'userProfile', _id: userId, cusData: {
                $addToSet: { thumbsUp: blogId },
                $pull: { thumbsDown: blogId },
            },
        });
        console.log("更新用户表", result);
        res.json({ message: '点赞成功', code: 0 });
    })
}

const thumbsDownBlog = (updateOne) => (req, res) => {
    let data = '';
    req.on('data', (chunk) => { data += chunk });
    req.on('end', async () => {
        const { userId, blogId } = JSON.parse(data);
        await updateOne({
            collection, _id: blogId, cusData: {
                $inc: { thumbsDown: 1 },
            },
        })
        await updateOne({
            collection: 'userProfile', _id: userId, cusData: {
                $addToSet: { thumbsDown: blogId },
                // thumbsUp: { $in: [blogId,'1222'] }// 删除多个
                $pull: { thumbsUp: blogId },
            }
        });
        res.json({ message: '点踩成功', code: 0 });
    })
}


//获取博客详情  用于 编辑/浏览博客
//每获取博客详情一次表示用户浏览一次 自动browseCount +1
const getBlogById = (aggregate, updateOne) => async (req, res) => {
    const { id: _id } = req.query;

    const pipeline = [
        { $match: { _id: ObjectId(_id) } },
        {
            $lookup: {
                from: userProfileCollection,
                localField: 'userId',
                foreignField: '_id',
                as: 'sender'
            }
        },
        {
            $project: {
                'sender.password': 0,
                'sender.lastUpdate': 0,
                'sender.thumbsUp': 0,
                'sender.thumbsDown': 0,
                'sender.mail': 0,
                "introduce": 0
            }
        }
    ];

    const blogs = await aggregate({ collection: 'blog', pipeline });

    if (blogs.length) {
        res.json(blogs[0]);
        updateOne({
            collection, _id, cusData: {
                $inc: { browseCount: 1 }
            },
        });
    }
    else res.status(500).json({ message: '博文获取失败' });
}

// 获取所有 博客标签列表
const getAllBlogTags = (findData) => async (req, res) => {
    const tags = await findData({ collection: 'blogTag', limit: 100 });
    if (tags) {
        res.json(tags);
    } else res.status(500).json({ message: '获取tag列表失败' });
}

//查询博客列表 
//TODO limit 分页功能 限制数量 排序
/**
 * @description: 请求blog列表
 * @param {number}  skip 跳过 从多少条开始
 * @param {boolean} self  只查询自己的博客
 * @param {boolean} verify 是否验证auth
 * @param {...} ...
 */
const getBlogs = (aggregate, getCollectionCounts, decrypt) => async (req, res) => {
    let { limit = 20, sort, skip = 0, self, find, verify } = req.query;
    limit = limit ? Number(limit) : limit;
    skip && (skip = Number(skip));
    sort = sort ? JSON.parse(sort) : { createAt: -1 };
    find = find ? JSON.parse(find) : {};
    console.log("query:", req.query);
    // console.log("query:", req.query);
    const { headers: { authorization } } = req;
    if (verify !== 'false' && authorization) {
        const userInfo = await decrypt(authorization);
        //管理员只获取自己的博客 admin获取全部 前端页面获取全部
        if (!userInfo) return;
        if (userInfo.role === 1 || self === 'true') {
            find['userId'] = ObjectId(userInfo._id);
        }
    }
    // 所有满足条件的数量  博客返回限制 limit 默认20条
    const counts = await getCollectionCounts({ collection, find });
    // const blogs = await findData({ collection, find, limit, sort, skip, project: { "content": 0 } });
    const pipeline = [
        { $match: find },
        {
            $lookup: {
                from: userProfileCollection,
                localField: 'userId',
                foreignField: '_id',
                as: 'sender'
            }
        },
        {
            $project: {
                'sender.password': 0,
                'sender.lastUpdate': 0,
                'sender.thumbsUp': 0,
                'sender.thumbsDown': 0,
                'sender.mail': 0,
                "content": 0
            }
        },
        { $sort: sort },
        { $skip: skip },
        { $limit: limit },
    ];
    const blogs = await aggregate({ collection, pipeline });

    //分页功能使用 skip -- 从多少开始查  limit--查多少个  sort -- 排序(待实现)
    // 获取列表是不返回 content 防止内容过大 只返回前200个字 的字段  introduce
    if (!blogs) return res.status(500).json({ message: '查询失败' });
    res.json({ counts, blogs });
}

/*
type: BlogType;
status: BlogStatus;
tags: string[];
reprintAddress?: string;
title: string;
content: string;
thumbsUp:number;
thumbsDown: number;
*/

//标签操作
//有-> +1
//没有 -> 插入
const addTag = ({ findData, insertOne, updateOne }) => (tags) => {
    tags.forEach(async (tag) => {
        const tagItem = await findData({
            collection: 'blogTag', find: { 'name': tag }
        });
        if (tagItem.length) {
            const [{ _id, count }] = tagItem;
            const result = await updateOne({ collection: 'blogTag', _id, data: { count: count + 1 } });
            console.log("update result:", result);
        } else {
            const data = { name: tag, count: 1 };
            const insert = await insertOne('blogTag', data);
            console.log("insert result:", insert);
        }
    })
}

const minusTag = ({ updateOne }) => (tags) => {
    tags.forEach(async (tag) => {
        updateOne({
            collection,
            cusData: {
                $inc: { count: -1 }
            },
            cusFilter: { name: tag }
        })
    })
}

const updateTag = ({ updateOne }) => (currTags, prevTags) => {
    const tags = {};
    for (const tag of currTags) {
        !(prevTags.includes(tag)) && (tags[tag] = 1);
    }
    for (const tag of prevTags) {
        !(currTags.includes(tag)) && (tags[tag] = -1);
    }
    console.log(tags);
    for (const [tag, count] of Object.entries(tags)) {
        updateOne({
            collection: 'blogTag',
            cusData: { $inc: { count } },
            cusFilter: { name: tag }
        })
    }
}

// 删除博客
const deleteBlog = (deleteOne, updateOne) => (req, res) => {
    //删除博客 tag -1
    let data = '';
    req.on('data', (chunk) => { data += chunk });
    req.on('end', async () => {
        const { _id, tags, blogOwner } = JSON.parse(data);
        const { role, _id: user } = req.userInfo;
        if (role === 2 || blogOwner === user) {
            const result = await deleteOne(collection, _id);
            if (result) {
                res.json({ message: "删除成功" });
                //delete tags 
                minusTag({ updateOne })(tags);
            }
            else res.status(500).json({ message: '删除失败' })
        }
    })
}

//发布博客  涵盖更新博客功能
const releaseBlog = ({ findData, insertOne, updateOne }) => (req, res) => {
    let data = '';
    req.on('data', (chunk) => { data += chunk });
    req.on('end', async () => {
        // req.userInfo 是 jwt验证时加入的 供后续使用
        // req.userInfo._id  保存 id 用户名可以改变id不变
        const blog = JSON.parse(data);
        console.log("blog:", blog);
        // 有 _id , userId , browseCount , createAt, updateAt, prevTags 视为 更新
        // 更新操作需要更新 tag  带上原来的 tags
        const isUpdate = blog._id && blog.userId && blog.prevTags;
        const now = Date.now();
        //简短的介绍 只在获取blog列表时返回 获取详情时 不返回
        const introduce = blog.content.substr(0, 200);
        let result;
        if (isUpdate) {
            const { _id, prevTags, updateAt, ...props } = blog;
            const newBlog = { ...props, updateAt: now, introduce, userId: ObjectId(blog.userId) };
            console.log("update", newBlog);
            result = await updateOne({ collection, _id, data: newBlog });
        } else {
            //创建博客
            const defaultBlogProps = { createAt: now, updateAt: now, browseCount: 0, thumbsUp: 0, thumbsDown: 0, commentCount: 0 };
            const payload = { ...blog, ...defaultBlogProps, userId: ObjectId(req.userInfo._id), introduce };
            result = await insertOne(collection, payload);
        }
        if (result) {
            console.log("release blog success:", result);
            res.json({ code: 0, message: '发布成功' });
            isUpdate ? updateTag({ updateOne })(blog.tags, blog.prevTags) : addTag({ findData, insertOne, updateOne })(blog.tags);
        } else
            res.status(500).json({ message: '发布失败' });

    })
}

//上传图片 
const uploadFile_md = (req, res) => {
    const {
        files: [{ path, mimetype, filename }],
    } = req;
    console.log("files:", req.files);
    // mimetype: 'image/png',
    // filename: '846764f3318fb3d40ee80c343b42bf29',
    //# 避免中文名 容易出现特殊字符请求不到文件
    const extName = mimetype.match(/\/(\w+)$/)[1];
    //# 不改名 也可以获取图片 不过 浏览器里输入地址就查看不到图片 而是下载文件了
    fs.rename(path, `${path}.${extName}`, (err) => {
        if (err) {
            console.error("fs rename err:", err);
            res.status(500).send({ message: "文件保存失败" });
        } else {
            const fileName = `${filename}.${extName}`;
            console.log("图片保存成功:", fileName);
            res.json(fileName);
        }
    });
}

module.exports = { uploadFile_md, releaseBlog, getBlogs, getAllBlogTags, getBlogById, deleteBlog, thumbsUpBlog, thumbsDownBlog, queryBlogThumbsState, cancelThumbs, transformThumbs };
//TODO  tags 的 更新